using System;
using System.Collections.Generic;
using System.Text;

namespace ErlyThreadsWinFormsDemo
{
	static class FibonacciProcess
	{
		/// <summary>
		/// a message that asks the thread to quit itself
		/// </summary>
		[Serializable]
		public class PleaseQuitMessage { }

		/// <summary>
		/// represents the request to calculate the nth fibonacci number
		/// </summary>
		[Serializable]
		public class PleaseCalculateNthFibonacciNumberMessage
		{
			public PleaseCalculateNthFibonacciNumberMessage(int pNthFibonacciNumber)
			{
				mNthFibonacciNumber = pNthFibonacciNumber;
			}

			private readonly int mNthFibonacciNumber;
			public int NthFibonacciNumber
			{
				get { return mNthFibonacciNumber; }
			}
		}

		/// <summary>
		/// represents the nth fibonacci number (the answer)
		/// </summary>
		[Serializable]
		public class NthFibonacciNumberMessage : PleaseCalculateNthFibonacciNumberMessage
		{
			public NthFibonacciNumberMessage(int pNthFibonacciNumber, long pAnswer) : base(pNthFibonacciNumber)
			{
				mAnswer = pAnswer;	
			}

			private readonly long mAnswer;
			public long Answer
			{
				get { return mAnswer; }
			}
		}

		/// <summary>
		/// The code that will execute on a dedicated thread when set up like this in the erly threads library
		/// </summary>
		/// <param name="pProcessHandle">The process handle.</param>
		public static void FibonacciThreadCode(ErlyThreads.IProcessHandle pProcessHandle)
		{
			bool mustQuit = false;
			while (!mustQuit)
			{
				// waits for any messages to come into the thread
				pProcessHandle.WaitForMessages();

				// "receives" the messages
				pProcessHandle.DispatchQueuedMessages(delegate(ErlyThreads.ReceivedMessage incomingMessage)
				{
					if (mustQuit) { return; }

					// handle the quit message first
					if (incomingMessage.Message.GetType().Equals(typeof(PleaseQuitMessage)))
					{
						mustQuit = true;
						return;
					}

					// handle the calculation message next
					else if (incomingMessage.Message.GetType().Equals(typeof(PleaseCalculateNthFibonacciNumberMessage)))
					{
						// get a handle on the incoming message for easy use
						PleaseCalculateNthFibonacciNumberMessage request = incomingMessage.Message as PleaseCalculateNthFibonacciNumberMessage;

						// this does the actual work, and may also take very long
						long nthFibonaccy = CalculateNthFibonacci(request.NthFibonacciNumber);
						
						// the replying message that will be sent back
						NthFibonacciNumberMessage replyMessage = new NthFibonacciNumberMessage(
							request.NthFibonacciNumber,
							nthFibonaccy);

						// send the reply back to the sender
						pProcessHandle.SendMessage(
							incomingMessage.SenderId,
							new NthFibonacciNumberMessage(
								request.NthFibonacciNumber,
								nthFibonaccy));
					}

					// ignore any other kind of message
					else
					{
					}
				});
			}
		}

		/// <summary>
		/// Calculates the NTH fibonacci.
		/// </summary>
		/// <remarks>not the best implimentation but follows rather easy from the definition. <see cref="http://en.wikipedia.org/wiki/Fibonacci_number"/></remarks>
		/// <returns>The nth fibonacci number</returns>
		private static long CalculateNthFibonacci(int pNth)
		{
			if (pNth <= 0) { return 0; }
			else if (pNth == 1) { return 1; }
			else return CalculateNthFibonacci(pNth - 1) + CalculateNthFibonacci(pNth - 2);
		}
	}
}
